package perform.type;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import perform.annotations.ManyToMany;
import perform.annotations.ManyToOne;
import perform.annotations.OneToMany;
import perform.annotations.OneToOne;
import perform.event.LoadEvent;
import perform.event.PerformEventHandler;
import perform.event.UpdateEvent;
import perform.mappings.ClassMappings;

@SuppressWarnings("serial")
public abstract class Type implements Serializable {

	private boolean saved;

	private PerformEventHandler eventHandler;
	private Map<String, Boolean> inits;

	protected Type() {
		saved = false;
		eventHandler = new PerformEventHandler();
		inits = new HashMap<String, Boolean>();
		Field[] fields = this.getClass().getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			if (fields[i].isAnnotationPresent(ManyToMany.class)
					|| fields[i].isAnnotationPresent(ManyToOne.class)
					|| fields[i].isAnnotationPresent(OneToMany.class)
					|| fields[i].isAnnotationPresent(OneToOne.class)) {
				inits.put(fields[i].getName(), false);
			}
		}
	}

	protected final void load(String fieldName) {
		if (saved && !inits.get(fieldName)) {
			try {
				eventHandler.handleLoadEvent(new LoadEvent(this, this
						.getClass().getDeclaredField(fieldName)));
				inits.put(fieldName, true);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	protected final <T extends Type> void add(String fieldName, T value) {
		if (saved) {
			try {
				eventHandler.handleAddEvent(new UpdateEvent<T>(this, this
						.getClass().getDeclaredField(fieldName), value));
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	protected final <T extends Type> void add(String fieldName, List<T> values) {
		if (saved) {
			try {
				eventHandler.handleAddEvent(new UpdateEvent<T>(this, this
						.getClass().getDeclaredField(fieldName), values));
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	protected final <T extends Type> void remove(String fieldName, T value) {
		if (saved) {
			try {
				eventHandler.handleRemoveEvent(new UpdateEvent<T>(this, this
						.getClass().getDeclaredField(fieldName), value));
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	protected final <T extends Type> void remove(String fieldName, List<T> values) {
		if (saved) {
			try {
				eventHandler.handleRemoveEvent(new UpdateEvent<T>(this, this
						.getClass().getDeclaredField(fieldName), values));
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	final boolean isSaved() {
		return saved;
	}

	final void setSaved(boolean saved) {
		this.saved = saved;
	}

	@Override
	public boolean equals(Object obj) {
		try {
			if (obj != null && this.getClass().equals(obj.getClass())) {
				Field idField = ClassMappings.getIDField(this.getClass());
				if (idField.get(this).equals(idField.get(obj))) {
					return true;
				}
			}
			return false;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

}
